package com.adee.java;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class Test033_Generic {
    public static void main(String[] args) throws Exception {
        test6();
        test7();
    }

    // 泛指研究工作
    @FunctionalInterface
    interface Research<IN,OUT> {
        OUT process(IN in);
    }
    // 计算机研究
    static class ComputerResearch implements Research<Time,Paper> {
        @Override
        public Paper process(Time in) {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) { }
            Paper paper = new Paper().setTitle("mapper-reduce算法")
                    .setCategory("流程算法")
                    .setDetails("流程规范算法，将流程划分为两步：mapper和reduce，mapper的结果传给reduce，由reduce处理后得到最终结果")
                    .setCollusion("mapper-reduce算法可以规范流程");
            return paper;
        }
    }
    // 流行病研究
    @Slf4j
    static class EpidemicResearch implements Research<Object,String> {
        @Override
        public String process(Object s) {

            return "epidemic research " + s;
        }
    }
    // 有基本研究能力的
    static interface BasicResearchCapable<T,R> {
        Class<? extends Research<T,R>> researchType(); // 可以做哪种研究
        R doResearch(T in) throws Exception; // 做研究工作，得出研究成果
    }
    // 抽象研究员
    @Slf4j
    static abstract class AbstractResearcher<T,R> implements BasicResearchCapable<T,R> {
        //private static final logger
        protected Class<? extends Research<T,R>> researchType;
        // 已声明研究类型
        public AbstractResearcher(Class<? extends Research<T,R>> researchClass) {
            researchType = researchClass;
        }
        // 未声明的研究类型，根据入参出参创建匿名研究类型
        public AbstractResearcher(Function<T,R> func) {
            // 根据入参、出参类型创建一个匿名类
            Research<T, R> anonymousResearch = new Research<T, R>() {
                @Override
                public R process(T t) {
                    return func.apply(t);
                }
            };
            researchType = (Class<? extends Research<T,R>>)anonymousResearch.getClass();
        }
        @Override
        public Class<? extends Research<T,R>> researchType() {
            return researchType;
        }

        @Override
        public final R doResearch(T in) throws Exception {
            // 每次执行doResearch方法时，内部都会创建一个新的research对象
            Constructor<? extends Research<T, R>> con = null;
            Research<T, R> research = null;
            try {
                con = researchType.getDeclaredConstructor();
                research = con.newInstance();
            } catch (NoSuchMethodException e) {
                log.error("{} has no default constructor", researchType.getName());
                try {
                    con = researchType.getDeclaredConstructor(in.getClass());
                    research = con.newInstance(in);
                } catch (NoSuchMethodException ex) {
                    log.error("{} has no constructor({},{})",
                            researchType.getName(),
                            in.getClass().getName());
                    throw ex;
                }
            }


            //Research<T, R> research = researchType.getConstructor().newInstance();
            prepare(research);
            R r = conduct(research, in);
            post(r);
            return r;
        }

        protected void prepare(Research<T, R> research) {
            log.debug("prepare before conduct...");
        }
        protected abstract R conduct(Research<T, R> research, T in) throws Exception;
        protected void post(R out) {
            log.debug("post conduct...");
        }
    }
    // 默认研究员
    static class DefaultResearcher<T,R> extends AbstractResearcher<T,R> {

        public DefaultResearcher(Class<? extends Research<T, R>> researchClass) {
            super(researchClass);
        }

        public DefaultResearcher(Function<T, R> func) {
            super(func);
        }

        @Override
        protected R conduct(Research<T, R> research, T in) throws Exception {
            return research.process(in);
        }
    }
    // 具体研究员：计算机研究员
    static class ComputerResearcher extends AbstractResearcher<Time, Paper> {

        public ComputerResearcher(Class<? extends Research<Time, Paper>> researchClass) {
            super(researchClass);
        }

        @Override
        protected Paper conduct(Research<Time, Paper> research, Time in) throws Exception {
            Paper paper = research.process(in.add(60)); // 做研究天数in，再加上论文整理等其他时间，算为60天
            return paper.stamp(); // 给论文盖戳，再发出去
        }
    }

    // 使用1，实现定义好的Research
    public static void test6() throws Exception {
        ComputerResearcher computerResearcher = new ComputerResearcher(ComputerResearch.class);
        Paper paper = computerResearcher.doResearch(new Time(100));
        System.out.println(paper.toString());
    }

    // 使用2，Lambda匿名Research
    public static void test7() throws Exception {
        DefaultResearcher<String, Map<String, Object>> stringMapDefaultResearcher
                = new DefaultResearcher<String, Map<String, Object>>(in -> {
            Map<String,Object> map = new HashMap<>();
            map.put("researchName", in);
            map.put("length", in.length());
            return map;
        });
        Map<String, Object> r = stringMapDefaultResearcher.doResearch("MarketResearch in Langfang");
        System.out.println(r);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Time {
        private int days;
        public Time add(int inc) {
            days += inc;
            return this;
        }
    }
    @Accessors(chain = true)
    @Data
    @ToString
    static class Paper {
        private String title;
        private String category;
        private String details;
        private String collusion;
        private long timestamp;
        public Paper stamp() {
            timestamp = System.currentTimeMillis();
            return this;
        }
    }
}
